<template>
	<div class="battle-container" :style="battleContainerStyle">
		<!-- 顶部控制栏 -->
		<div class="battle-header">
			<div class="battle-title">回合制战斗</div>
			<!-- 回合数显示 -->
			<div class="round-display">第{{ currentRound }}回合</div>
			<div class="battle-controls">
				<button class="speed-btn" @click="toggleSpeed" :class="{ active: isSpeedUp }">
					{{ isSpeedUp ? '2x' : '1x' }}
				</button>
				<!-- 新增：全局速度倍率选择 -->
				<select class="speed-select" v-model.number="currentSpeed" @change="onSpeedChange">
					<option v-for="opt in speedOptions" :key="'spd-'+opt.value" :value="opt.value">{{ opt.label }}</option>
				</select>
				<button class="back-btn" @click="goBack">返回</button>
			</div>
		</div>

		<!-- 敌方区域 -->
		<div class="enemy-area">
			<div class="area-title">敌方</div>
			<div class="units-container">
				<div class="unit-row front-row">
					<div 
						v-for="(enemy, index) in frontEnemies" 
						:key="'enemy-front-' + index"
						:class="['unit', enemy.isPlaceholder ? 'placeholder' : 'enemy-unit', { defeated: enemy.hp <= 0, 'using-skill': enemy.isUsingSkill, 'actioning': enemy.actionActive }]"
						:style="{ animationDelay: index * 0.1 + 's' }"
						:data-unit-id="enemy.isPlaceholder ? null : enemy.id"
					:data-position="enemy.position"
					@click="openAttrModal(enemy)"
				>
						<template v-if="!enemy.isPlaceholder">
							<div class="unit-name">{{ enemy.name }}</div>
							<div class="unit-hp">
								<div class="hp-bar">
									<div class="hp-fill" :style="{ width: (enemy.hp / enemy.maxHp * 100) + '%' }"></div>
								</div>
								<span class="hp-text">{{ enemy.hp }}/{{ enemy.maxHp }}</span>
							</div>
							<div v-if="enemy.damageText" class="damage-text" :key="enemy.damageKey" :class="{ 'crit-damage': enemy.isCrit }">
								-{{ enemy.damageText }}
							</div>
                            <div v-if="enemy.resistHint" class="resist-hint">{{ enemy.resistHint }}</div>
							<div v-if="enemy.healText" class="heal-text" :key="enemy.healKey">
								+{{ enemy.healText }}
							</div>
							<!-- 状态效果显示 -->
							<div v-if="enemy.statusEffects && enemy.statusEffects.length" class="status-effects">
								<div 
									v-for="(effect, effectIndex) in enemy.statusEffects" 
									:key="'enemy-front-effect-' + effectIndex"
									class="status-effect"
									:title="effect.name || effect"
								>
									{{ effect.name || effect }}
								</div>
							</div>
							<div v-if="enemy.skillName" class="skill-name">
                        <span class="skill-name-text">{{ enemy.skillName }}</span>
                    </div>
                    <div v-if="enemy.buffPopups && enemy.buffPopups.length" class="buff-stack">
                        <div v-for="(bp, i) in enemy.buffPopups" :key="bp.key" class="buff-item">
                            <div v-if="i > 0" class="buff-divider"></div>
                            <div class="buff-popup">{{ bp.text }}</div>
                        </div>
                    </div>
						</template>
					</div>
				</div>
				<div class="unit-row back-row">
					<div 
						v-for="(enemy, index) in backEnemies" 
						:key="'enemy-back-' + index"
						:class="['unit', enemy.isPlaceholder ? 'placeholder' : 'enemy-unit', { defeated: enemy.hp <= 0, 'actioning': enemy.actionActive }]"
						:style="{ animationDelay: (index + 2) * 0.1 + 's' }"
						:data-unit-id="enemy.isPlaceholder ? null : enemy.id"
					:data-position="enemy.position"
					@click="openAttrModal(enemy)"
				>
						<template v-if="!enemy.isPlaceholder">
							<div class="unit-name">{{ enemy.name }}</div>
							<div class="unit-hp">
								<div class="hp-bar">
									<div class="hp-fill" :style="{ width: (enemy.hp / enemy.maxHp * 100) + '%' }"></div>
								</div>
								<span class="hp-text">{{ enemy.hp }}/{{ enemy.maxHp }}</span>
							</div>
							<div v-if="enemy.damageText" class="damage-text" :key="enemy.damageKey">
								-{{ enemy.damageText }}
							</div>
                            <div v-if="enemy.resistHint" class="resist-hint">{{ enemy.resistHint }}</div>
							<!-- 状态效果显示 -->
							<div v-if="enemy.statusEffects && enemy.statusEffects.length" class="status-effects">
								<div 
									v-for="(effect, effectIndex) in enemy.statusEffects" 
									:key="'enemy-effect-' + effectIndex"
									class="status-effect"
									:title="effect.name || effect"
								>
									{{ effect.name || effect }}
									</div>
							</div>
							<div v-if="enemy.skillName" class="skill-name">
                        <span class="skill-name-text">{{ enemy.skillName }}</span>
                    </div>
                    <div v-if="enemy.buffPopups && enemy.buffPopups.length" class="buff-stack">
                        <div v-for="(bp, i) in enemy.buffPopups" :key="bp.key" class="buff-item">
                            <div v-if="i > 0" class="buff-divider"></div>
                            <div class="buff-popup">{{ bp.text }}</div>
                        </div>
                    </div>
						</template>
					</div>
				</div>
			</div>
		</div>

		<!-- 实时行动日志展示区 -->
		<div class="action-log-display">
			<div class="action-log-content" v-if="currentActionLog">
				<div class="action-round-info" v-if="currentActionLog.isRoundStart">
					第{{ currentActionLog.round }}回合开始
				</div>
				<div class="action-round-info" v-else-if="currentActionLog.isRoundEnd">
					第{{ currentActionLog.round }}回合结束
				</div>
				<div class="action-actor-info" v-else>
					<span class="actor-name">{{ currentActionLog.actorName }}</span>
					<span class="action-type" :class="'type-' + currentActionLog.actionType">
						{{ currentActionLog.actionType }}
					</span>
					<span class="action-skill" v-if="currentActionLog.skillName">
						{{ currentActionLog.skillName }}
					</span>
				</div>
				<div class="action-description">
					{{ currentActionLog.description }}
				</div>
			</div>
			<div class="action-log-placeholder" v-else>
				等待行动...
			</div>
			<!-- 战斗播报按钮 -->
			<div class="battle-log-button-wrapper">
				<button class="log-toggle-btn-compact" @click="toggleLogModal">
					<span class="log-icon">📜</span>
					<span v-if="newLogCount > 0" class="log-badge-compact">{{ newLogCount }}</span>
				</button>
			</div>
		</div>

		<!-- 我方区域 -->
		<div class="player-area">
			<div class="area-title">我方</div>
			<div class="units-container">
				<div class="unit-row front-row">
					<div 
						v-for="(ally, index) in frontAllies" 
						:key="'ally-front-' + index"
						:class="['unit', ally.isPlaceholder ? 'placeholder' : 'ally-unit', { defeated: ally.hp <= 0, 'using-skill': ally.isUsingSkill, 'actioning': ally.actionActive }]"
						:style="{ animationDelay: index * 0.1 + 's' }"
						:data-unit-id="ally.isPlaceholder ? null : ally.id"
					:data-position="ally.position"
					@click="openAttrModal(ally)"
				>
						<template v-if="!ally.isPlaceholder">
							<div class="unit-name">{{ ally.name }}</div>
							<div class="unit-hp">
								<div class="hp-bar">
									<div class="hp-fill" :style="{ width: (ally.hp / ally.maxHp * 100) + '%' }"></div>
								</div>
								<span class="hp-text">{{ ally.hp }}/{{ ally.maxHp }}</span>
							</div>
							<div v-if="ally.damageText" class="damage-text" :key="ally.damageKey" :class="{ 'crit-damage': ally.isCrit }">
								-{{ ally.damageText }}
							</div>
                            <div v-if="ally.resistHint" class="resist-hint">{{ ally.resistHint }}</div>
							<div v-if="ally.healText" class="heal-text" :key="ally.healKey">
								+{{ ally.healText }}
							</div>
							<div v-if="ally.skillName" class="skill-name">
                        <span class="skill-name-text">{{ ally.skillName }}</span>
                    </div>
                    <div v-if="ally.buffPopups && ally.buffPopups.length" class="buff-stack">
                        <div v-for="(bp, i) in ally.buffPopups" :key="bp.key" class="buff-item">
                            <div v-if="i > 0" class="buff-divider"></div>
                            <div class="buff-popup">{{ bp.text }}</div>
                        </div>
                    </div>
							<!-- 状态效果显示 -->
							<div v-if="ally.statusEffects && ally.statusEffects.length" class="status-effects">
								<div 
									v-for="(effect, effectIndex) in ally.statusEffects" 
									:key="'ally-effect-' + effectIndex"
									class="status-effect"
									:title="effect.name || effect"
								>
									{{ effect.name || effect }}
								</div>
							</div>
						</template>
					</div>
				</div>
				<div class="unit-row back-row">
					<div 
						v-for="(ally, index) in backAllies" 
						:key="'ally-back-' + index"
						:class="['unit', ally.isPlaceholder ? 'placeholder' : 'ally-unit', { defeated: ally.hp <= 0, 'actioning': ally.actionActive }]"
						:style="{ animationDelay: (index + 2) * 0.1 + 's' }"
						:data-unit-id="ally.isPlaceholder ? null : ally.id"
					:data-position="ally.position"
					@click="openAttrModal(ally)"
				>
						<template v-if="!ally.isPlaceholder">
							<div class="unit-name">{{ ally.name }}</div>
							<div class="unit-hp">
								<div class="hp-bar">
									<div class="hp-fill" :style="{ width: (ally.hp / ally.maxHp * 100) + '%' }"></div>
								</div>
								<span class="hp-text">{{ ally.hp }}/{{ ally.maxHp }}</span>
							</div>
							<div v-if="ally.damageText" class="damage-text" :key="ally.damageKey">
                            -{{ ally.damageText }}
                        </div>
                        <div v-if="ally.resistHint" class="resist-hint">{{ ally.resistHint }}</div>
						<!-- 状态效果显示 -->
								<div v-if="ally.statusEffects && ally.statusEffects.length" class="status-effects">
									<div 
										v-for="(effect, effectIndex) in ally.statusEffects" 
										:key="'ally-effect-' + effectIndex"
										class="status-effect"
										:title="effect.name || effect"
									>
										{{ effect.name || effect }}
										</div>
								</div>
							<div v-if="ally.buffPopupText" class="buff-popup" :key="ally.buffPopupKey">{{ ally.buffPopupText }}</div>
						</template>
					</div>
				</div>
			</div>
		</div>

		<!-- 战斗日志弹窗 -->
		<div v-if="showLogModal" class="log-modal-backdrop" @click="closeLogModal">
			<div class="log-modal" @click.stop>
				<div class="log-modal-header">
					<span class="modal-title">战斗播报</span>
					<button class="modal-close" @click="closeLogModal">×</button>
				</div>
				<div class="log-modal-content">
					<div v-for="(log, index) in battleLogs" :key="index" class="log-item">
						{{ log }}
					</div>
					<div v-if="!battleLogs.length" class="log-empty">暂无播报</div>
				</div>
			</div>
		</div>

		<!-- 攻击连线 -->
		<svg class="attack-lines" v-if="attackLines && attackLines.length">
			<defs>
				<!-- 武侠风格攻击技能渐变 -->
				<linearGradient id="allyAttackStroke" x1="0%" y1="0%" x2="100%" y2="0%">
					<stop offset="0%" style="stop-color:#dc143c;stop-opacity:1" />
					<stop offset="50%" style="stop-color:#ff6b35;stop-opacity:1" />
					<stop offset="100%" style="stop-color:#dc143c;stop-opacity:1" />
				</linearGradient>
				<!-- 武侠风格辅助技能渐变 -->
				<linearGradient id="allySupportStroke" x1="0%" y1="0%" x2="100%" y2="0%">
					<stop offset="0%" style="stop-color:#32cd32;stop-opacity:1" />
					<stop offset="50%" style="stop-color:#98fb98;stop-opacity:1" />
					<stop offset="100%" style="stop-color:#32cd32;stop-opacity:1" />
				</linearGradient>
				<!-- 武侠风格控制技能渐变 -->
				<linearGradient id="allyControlStroke" x1="0%" y1="0%" x2="100%" y2="0%">
					<stop offset="0%" style="stop-color:#8a2be2;stop-opacity:1" />
					<stop offset="50%" style="stop-color:#da70d6;stop-opacity:1" />
					<stop offset="100%" style="stop-color:#8a2be2;stop-opacity:1" />
				</linearGradient>
				<!-- 武侠风格普通攻击渐变 -->
				<linearGradient id="allyNormalStroke" x1="0%" y1="0%" x2="100%" y2="0%">
					<stop offset="0%" style="stop-color:#d4af37;stop-opacity:1" />
					<stop offset="50%" style="stop-color:#ffd700;stop-opacity:1" />
					<stop offset="100%" style="stop-color:#d4af37;stop-opacity:1" />
				</linearGradient>
				<!-- 敌方攻击技能渐变 -->
				<linearGradient id="enemyAttackStroke" x1="0%" y1="0%" x2="100%" y2="0%">
					<stop offset="0%" style="stop-color:#b22222;stop-opacity:1" />
					<stop offset="50%" style="stop-color:#ff4500;stop-opacity:1" />
					<stop offset="100%" style="stop-color:#b22222;stop-opacity:1" />
				</linearGradient>
				<!-- 敌方辅助技能渐变 -->
				<linearGradient id="enemySupportStroke" x1="0%" y1="0%" x2="100%" y2="0%">
					<stop offset="0%" style="stop-color:#228b22;stop-opacity:1" />
					<stop offset="50%" style="stop-color:#90ee90;stop-opacity:1" />
					<stop offset="100%" style="stop-color:#228b22;stop-opacity:1" />
				</linearGradient>
				<!-- 敌方控制技能渐变 -->
				<linearGradient id="enemyControlStroke" x1="0%" y1="0%" x2="100%" y2="0%">
					<stop offset="0%" style="stop-color:#663399;stop-opacity:1" />
					<stop offset="50%" style="stop-color:#ba55d3;stop-opacity:1" />
					<stop offset="100%" style="stop-color:#663399;stop-opacity:1" />
				</linearGradient>
				<!-- 敌方普通攻击渐变 -->
				<linearGradient id="enemyNormalStroke" x1="0%" y1="0%" x2="100%" y2="0%">
					<stop offset="0%" style="stop-color:#b8860b;stop-opacity:1" />
					<stop offset="50%" style="stop-color:#daa520;stop-opacity:1" />
					<stop offset="100%" style="stop-color:#b8860b;stop-opacity:1" />
				</linearGradient>
				<!-- 箭头标记，增强攻击方向感（更细小） -->
				<marker id="allyArrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto">
					<path d="M0,0 L8,4 L0,8 L2.5,4 Z" fill="#d4af37" />
				</marker>
				<marker id="enemyArrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto">
					<path d="M0,0 L8,4 L0,8 L2.5,4 Z" fill="#b8860b" />
				</marker>
			</defs>
			<line v-for="(line, idx) in attackLines" :key="'atk-line-' + idx"
				:x1="line.x1" 
				:y1="line.y1" 
				:x2="line.x2" 
				:y2="line.y2" 
				:stroke="getSkillLineColor(line)"
				:stroke-width="getSkillLineWidth(line)" 
				:stroke-dasharray="getSkillLineDash(line)" 
				class="attack-line"
				stroke-linecap="round"
				vector-effect="non-scaling-stroke"
				:marker-end="line.isAllyAttack ? 'url(#allyArrow)' : 'url(#enemyArrow)'"
			/>
		</svg>
		<svg class="attack-lines" v-else-if="attackLine">
			<defs>
				<linearGradient id="allyAttackGradient" x1="0%" y1="0%" x2="100%" y2="0%">
					<stop offset="0%" style="stop-color:#00b894;stop-opacity:1" />
					<stop offset="100%" style="stop-color:#00cec9;stop-opacity:1" />
				</linearGradient>
				<linearGradient id="enemyAttackGradient" x1="0%" y1="0%" x2="100%" y2="0%">
					<stop offset="0%" style="stop-color:#ff7675;stop-opacity:1" />
					<stop offset="100%" style="stop-color:#fd79a8;stop-opacity:1" />
				</linearGradient>
				<!-- 箭头标记，增强攻击方向感（更细小） -->
				<marker id="allyArrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto">
					<path d="M0,0 L8,4 L0,8 L2.5,4 Z" fill="#00cec9" />
				</marker>
				<marker id="enemyArrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto">
					<path d="M0,0 L8,4 L0,8 L2.5,4 Z" fill="#fd79a8" />
				</marker>
			</defs>
			<line 
				:x1="attackLine.x1" 
				:y1="attackLine.y1" 
				:x2="attackLine.x2" 
				:y2="attackLine.y2" 
				:stroke="attackLine.isAllyAttack ? 'url(#allyAttackStroke)' : 'url(#enemyAttackStroke)'" 
				stroke-width="3" 
				class="attack-line"
				stroke-linecap="round"
				vector-effect="non-scaling-stroke"
				:marker-end="attackLine.isAllyAttack ? 'url(#allyArrow)' : 'url(#enemyArrow)'"
			/>
		</svg>

		<!-- 战斗结果弹窗 -->
		<div v-if="battleResult" class="battle-result-modal">
			<div class="result-content">
				<h2 :class="battleResult.victory ? 'victory' : 'defeat'">
					{{ battleResult.victory ? '胜利！' : '失败！' }}
				</h2>
				<p>{{ battleResult.message }}</p>
				<button @click="restartBattle" class="restart-btn">重新战斗</button>
				<button @click="goBack" class="back-btn">返回主页</button>
			</div>
		</div>

		<!-- 属性弹窗 -->
		<div v-if="showAttrModal && selectedUnit" class="attr-modal" @click.self="closeAttrModal">
			<div class="attr-content">
				<h3 class="attr-title">{{ selectedUnit.name }}</h3>
				<div class="attr-grid">
					<div class="attr-item"><span>生命</span><span>{{ selectedUnit.hp }}/{{ selectedUnit.maxHp }}</span></div>
					<div class="attr-item"><span>攻击</span><span>{{ selectedUnit.baseDamage || selectedUnit.attack || 0 }}</span></div>
					<div class="attr-item"><span>防御</span><span>{{ selectedUnit.defense || 0 }}</span></div>
					<div class="attr-item"><span>速度</span><span>{{ selectedUnit.speed || 0 }}</span></div>
					<div class="attr-item"><span>命中</span><span>{{ selectedUnit.hitRate || 0 }}</span></div>
					<div class="attr-item"><span>闪避</span><span>{{ selectedUnit.dodgeRate || 0 }}</span></div>
					<div class="attr-item full"><span>状态</span><span>{{ (selectedUnit.statusEffects || []).map(s => s.name || s).join('、') || '无' }}</span></div>
				</div>
				<button class="close-btn" @click="closeAttrModal">关闭</button>
			</div>
		</div>
	</div>
</template>

<script>
import BattleEngine from '../../src/utils/battleEngine'

export default {
	data() {
		return {
			// 战斗引擎
			battleEngine: null,
			
			// 战斗控制
			isSpeedUp: false,
			battleTimer: null,
			turnInterval: 2500, // 回合基础间隔（ms）
			battleResult: null,
			isLoading: false,
			battlePaused: false, // 战斗暂停状态
			// 新增：全局速度倍率
			speedOptions: [
				{ label: '1x', value: 1 },
				{ label: '1.5x', value: 1.5 },
				{ label: '2x', value: 2 },
				{ label: '3x', value: 3 }
			],
			currentSpeed: 1,
			// 新增：异步循环控制
			isBattleLoopRunning: false,
			isPageActive: true,
			
			// 攻击动画（队列 + 单线，严格按出手顺序）
			attackLine: null,
			attackQueue: [],
			isProcessingAttack: false,
            attackLines: [],
            multiLineActive: false,
            pendingMultiDamage: {},
            // 技能目标动画队列（避免不同技能同时渲染重叠）
            skillTargetsQueue: [],
            isProcessingSkillTargets: false,
            // 每单位技能/BUFF提示队列（避免提示重叠）
            skillPromptQueues: {},
            isProcessingSkillPrompt: {},
            // 技能冷却时间（单位ID -> 时间戳），用于与目标动作间隔对齐
            skillCooldownUntil: {},
            // 连线防重锁（单位ID -> 锁定到期时间戳），避免重复绘制
            lineLockUntil: {},
			
			// 战斗日志
			battleLogs: [],
			showLogModal: false,
			newLogCount: 0,
			
			// 当前行动日志(实时展示)
			currentActionLog: null,
			
			// 战斗单位（从引擎同步）
			enemies: [],
			allies: [],
			// 属性弹窗
			showAttrModal: false,
			selectedUnitId: null,
			// 回合互斥锁
			isProcessingTurn: false
		}
	},
	
	computed: {
		// 前排敌人 (位置1-3) - 修复空位显示
		frontEnemies() {
			const front = this.enemies.filter(enemy => {
				const pos = enemy.position
				const posStr = String(pos || '')
				if (posStr.startsWith('front')) return true
				if (posStr.startsWith('back')) return false
				const posNum = Number(pos)
				return !isNaN(posNum) && posNum >= 1 && posNum <= 3
			}).sort((a,b) => Number(a.position) - Number(b.position))
			
			// 创建完整的3个位置数组，空位用占位符填充
			const positions = [1, 2, 3]
			const result = positions.map(pos => {
				const unit = front.find(enemy => Number(enemy.position) === pos)
				return unit || { 
					isPlaceholder: true, 
					type: 'enemy', 
					position: pos,
					id: `placeholder-enemy-front-${pos}`
				}
			})
			return result
		},
		// 选中单位（实时计算，确保数据更新）
		selectedUnit() {
			if (!this.selectedUnitId) return null
			return this.allies.concat(this.enemies).find(u => u && u.id === this.selectedUnitId) || null
		},
		
		// 后排敌人 (位置4-6) - 修复空位显示
		backEnemies() {
			const back = this.enemies.filter(enemy => {
				const pos = enemy.position
				const posStr = String(pos || '')
				if (posStr.startsWith('back')) return true
				if (posStr.startsWith('front')) return false
				const posNum = Number(pos)
				return !isNaN(posNum) && posNum >= 4 && posNum <= 6
			}).sort((a,b) => Number(a.position) - Number(b.position))
			
			// 创建完整的3个位置数组，空位用占位符填充
			const positions = [4, 5, 6]
			const result = positions.map(pos => {
				const unit = back.find(enemy => Number(enemy.position) === pos)
				return unit || { 
					isPlaceholder: true, 
					type: 'enemy', 
					position: pos,
					id: `placeholder-enemy-back-${pos}`
				}
			})
			return result
		},
		
		// 前排我方 (位置1-3) - 修复空位显示
		frontAllies() {
			const front = this.allies.filter(ally => {
				const pos = ally.position
				const posStr = String(pos || '')
				if (posStr.startsWith('front')) return true
				if (posStr.startsWith('back')) return false
				const posNum = Number(pos)
				return !isNaN(posNum) && posNum >= 1 && posNum <= 3
			}).sort((a,b) => Number(a.position) - Number(b.position))
			
			// 创建完整的3个位置数组，空位用占位符填充
			const positions = [1, 2, 3]
			const result = positions.map(pos => {
				const unit = front.find(ally => Number(ally.position) === pos)
				return unit || { 
					isPlaceholder: true, 
					type: 'ally', 
					position: pos,
					id: `placeholder-ally-front-${pos}`
				}
			})
			return result
		},
		
		// 后排我方 (位置4-6) - 修复空位显示
		backAllies() {
			const back = this.allies.filter(ally => {
				const pos = ally.position
				const posStr = String(pos || '')
				if (posStr.startsWith('back')) return true
				if (posStr.startsWith('front')) return false
				const posNum = Number(pos)
				return !isNaN(posNum) && posNum >= 4 && posNum <= 6
			}).sort((a,b) => Number(a.position) - Number(b.position))
			
			// 创建完整的3个位置数组，空位用占位符填充
			const positions = [4, 5, 6]
			const result = positions.map(pos => {
				const unit = back.find(ally => Number(ally.position) === pos)
				return unit || { 
					isPlaceholder: true, 
					type: 'ally', 
					position: pos,
					id: `placeholder-ally-back-${pos}`
				}
			})
			return result
		},
		
		// 存活的敌人
		aliveEnemies() {
			return this.enemies.filter(enemy => enemy.isAlive)
		},
		
		// 存活的我方
		aliveAllies() {
			return this.allies.filter(ally => ally.isAlive)
		},
		
		// 当前回合数
		currentRound() {
			return this.battleEngine ? this.battleEngine.battleState.currentRound : 0
		},
		
		// 战斗容器样式（用于控制动画速度）
    battleContainerStyle() {
      try {
        if (this.battleEngine && typeof this.battleEngine.getUiDurations === 'function') {
          const d = this.battleEngine.getUiDurations()
          const toUnit = (ms) => (ms >= 1000 ? (ms / 1000) + 's' : ms + 'ms')
          return {
            '--damage-duration': toUnit(d.damageMs),
            '--attack-duration': toUnit(d.attackMs),
            '--heal-duration': toUnit(d.healMs),
            '--buff-duration': toUnit(d.buffMs),
            '--skill-duration': toUnit(d.skillMs)
          }
        }
      } catch (e) {}
      // 回退
      return {
        '--damage-duration': '3s',
        '--attack-duration': '2s',
        '--heal-duration': '3.75s',
        '--buff-duration': '3.75s',
        '--skill-duration': '5s'
      }
    }
	},
	
	async mounted() {
		try {
			await this.initializeBattle()
		} catch (error) {
			console.error('战斗初始化错误:', error)
			uni.showToast({
				title: '战斗初始化失败',
				icon: 'error'
			})
			// 延迟返回，给用户看到错误提示
			setTimeout(() => {
				this.goBack()
			}, 2000)
		}
		// 绑定 ESC 键关闭属性弹窗
		if (typeof window !== 'undefined') {
			window.addEventListener('keydown', this.onKeydownEsc)
		}
		this.isPageActive = true
	},
	
	beforeDestroy() {
		if (this.battleTimer) {
			clearInterval(this.battleTimer)
		}
		if (typeof window !== 'undefined') {
			window.removeEventListener('keydown', this.onKeydownEsc)
		}
		// 标记页面已失活，循环自然停止
		this.isPageActive = false
		this.battlePaused = true
	},
	
	methods: {
		/**
		 * 初始化战斗
		 */
		async initializeBattle() {
			this.isLoading = true
			
			try {
				// 创建战斗引擎
				this.battleEngine = new BattleEngine()
				
				// 设置动画回调
				this.battleEngine.onDamageAnimation = this.showDamageAnimation.bind(this)
				this.battleEngine.onHealAnimation = this.showHealAnimation.bind(this)
				this.battleEngine.onSkillAnimation = this.showSkillAnimation.bind(this)
				this.battleEngine.onLog = (msg) => { this.addLog(msg) }
				// 新增：buff弹窗回调
				this.battleEngine.onBuffPopup = this.showBuffPopup.bind(this)
            // 新增：技能多目标连线回调
            this.battleEngine.onSkillTargets = this.showSkillTargets.bind(this)
                // 新增：控制抵抗提示回调
                this.battleEngine.onResistHint = this.showResistHint.bind(this)
				// 新增：当前行动日志回调
				this.battleEngine.onCurrentAction = this.showCurrentAction.bind(this)
				
				// 获取战斗参数...
				let battleType = 'pve'
				let targetId = 'monster_1'
				let battleIdParam = null
				// 路由参数读取（省略未改）...
				if (this.$route && this.$route.query) {
					battleType = this.$route.query.battleType || this.$route.query.type || 'pve'
					targetId = this.$route.query.targetId || this.$route.query.target || 'monster_1'
					battleIdParam = this.$route.query.battleId || null
				} else {
					try {
						if (typeof getCurrentPages === 'function') {
							const pages = getCurrentPages()
							if (pages.length > 0) {
								const currentPage = pages[pages.length - 1]
								if (currentPage.options) {
									battleType = currentPage.options.battleType || currentPage.options.type || 'pve'
									targetId = currentPage.options.targetId || currentPage.options.target || 'monster_1'
									battleIdParam = currentPage.options.battleId || null
								}
							}
						}
					} catch (e) {
						console.log('getCurrentPages不可用，使用默认参数')
					}
					// URL 参数回退（省略未改）...
					try {
						const urlParams = new URLSearchParams(window.location.search)
						if (urlParams.get('battleType')) {
							battleType = urlParams.get('battleType')
						} else if (urlParams.get('type')) {
							battleType = urlParams.get('type')
						}
						if (urlParams.get('targetId')) {
							targetId = urlParams.get('targetId')
						} else if (urlParams.get('target')) {
							targetId = urlParams.get('target')
						}
						if (urlParams.get('battleId')) {
							battleIdParam = urlParams.get('battleId')
						}
					} catch (e) {
						console.log('URL参数获取失败，使用默认参数')
					}
				}
				
				console.log('战斗参数:', { battleType, targetId, battleId: battleIdParam })
				
				// 初始化战斗数据
				const success = await this.battleEngine.initBattle(battleType, targetId)
				
				if (success) {
        // 初始化速度倍率（与选择器同步）：播放x倍 → 时长除以x
        if (typeof this.battleEngine.setSpeedMultiplier === 'function') {
          this.battleEngine.setSpeedMultiplier(1 / (this.currentSpeed || 1))
        }
					this.syncBattleState()
					this.addLog('战斗开始！')
					// 初始显示战斗开始文案
					this.currentActionLog = {
						isRoundStart: true,
						round: 0,
						description: '战斗开始！'
					}
					this.startBattle()
				} else {
					uni.showToast({
						title: '战斗初始化失败',
						icon: 'error'
					})
					this.goBack()
				}
			} catch (error) {
				console.error('战斗初始化错误:', error)
				uni.showToast({
					title: '战斗初始化失败',
					icon: 'error'
				})
				this.goBack()
			} finally {
				this.isLoading = false
			}
		},
		
		/**
		 * 同步战斗状态
		 */
		syncBattleState() {
			if (!this.battleEngine) {
				console.error('战斗引擎未初始化')
				return
			}
			
			const battleState = this.battleEngine.getBattleState()
			if (!battleState) {
				console.error('无法获取战斗状态')
				return
			}
			
			// 安全地更新单位状态
			this.allies = battleState.playerUnits || []
			this.enemies = battleState.enemyUnits || []
			
			// 增量更新战斗日志，保持与动画顺序一致
			const battleLog = battleState.battleLog || []
			const messages = battleLog.map(log => log.message || log)
			if (messages.length > this.battleLogs.length) {
				messages.slice(this.battleLogs.length).forEach(m => this.addLog(m))
			}
		},
		
		/**
		 * 开始战斗（改为 await 循环驱动）
		 */
		startBattle() {
            // 如果已暂停，不启动
            if (this.battlePaused) {
                return
            }
            // 停用旧的interval
            if (this.battleTimer) {
                clearInterval(this.battleTimer)
                this.battleTimer = null
            }
            // 启动异步循环
            this.runBattleLoop()
        },

        /**
         * 异步战斗循环：严格串行一个事件接一个事件
         */
        async runBattleLoop() {
            if (this.isBattleLoopRunning) return
            this.isBattleLoopRunning = true
            try {
                while (this.isPageActive && !this.battlePaused && this.battleEngine && !this.battleEngine.battleState.isFinished) {
                    await this.processTurn()
                    const speed = this.currentSpeed && this.currentSpeed > 0 ? this.currentSpeed : 1
                    const interval = Math.max(50, Math.floor(this.turnInterval / speed))
                    // 下一事件前的节拍等待
                    await new Promise(resolve => setTimeout(resolve, interval))
                }
            } finally {
                this.isBattleLoopRunning = false
            }
        },
		
		/**
		 * 处理回合
		 */
		async processTurn() {
			// 检查是否暂停
			if (this.battlePaused) {
				return
			}
			// 互斥：上一轮未完成则直接跳过，避免并发导致动画/文字错乱
			if (this.isProcessingTurn) {
				return
			}
			this.isProcessingTurn = true
			
			if (this.battleEngine.battleState.isFinished) {
				await this.endBattle()
				this.isProcessingTurn = false
				return
			}
			
			// 仅后端事件驱动
			await this.battleEngine.playNextEvent()
			
			// 同步战斗状态
			this.syncBattleState()
			
			// 检查战斗是否结束
			if (this.battleEngine.battleState.isFinished) {
				await this.endBattle()
			}
			this.isProcessingTurn = false
		},
		
		/**
		 * 切换速度（保留原快捷键：在1x和2x间切换）
		 */
		toggleSpeed() {
			this.isSpeedUp = !this.isSpeedUp
			this.currentSpeed = this.isSpeedUp ? 2 : 1
			// 应用到引擎
			try {
				if (this.battleEngine && typeof this.battleEngine.setSpeedMultiplier === 'function') {
					this.battleEngine.setSpeedMultiplier(1 / (this.currentSpeed || 1))
				}
			} catch (e) {}
			// 若循环未运行则重启
			if (!this.isBattleLoopRunning && !this.battlePaused) {
				this.runBattleLoop()
			}
		},
		
		/**
		 * 选择器切换速度
		 */
		onSpeedChange() {
			const speed = this.currentSpeed && this.currentSpeed > 0 ? this.currentSpeed : 1
			// 同步快捷按钮状态
			this.isSpeedUp = speed >= 2
			// 应用到引擎
			try {
				if (this.battleEngine && typeof this.battleEngine.setSpeedMultiplier === 'function') {
					this.battleEngine.setSpeedMultiplier(1 / speed)
				}
			} catch (e) {}
			// 若循环未运行则重启
			if (!this.isBattleLoopRunning && !this.battlePaused) {
				this.runBattleLoop()
			}
		},
		
		/**
		 * 切换战斗暂停状态
		 */
		toggleBattlePause() {
			this.battlePaused = !this.battlePaused
			console.log('[战斗控制]', this.battlePaused ? '战斗已暂停' : '战斗已继续')
			
			if (this.battlePaused) {
				// 停用旧的interval
				if (this.battleTimer) {
					clearInterval(this.battleTimer)
					this.battleTimer = null
				}
				// await 循环会自然停在下一拍
			} else {
				// 恢复战斗
				this.startBattle()
			}
		},
		
		/**
		 * 处理战斗动作动画
		 */
		handleBattleAction(action) {
			if (!action) return
			console.log('action.type:', action.type)
			switch (action.type) {
                case '攻击':
                    this.showDamageAnimation(action.target, action.value, action.isCrit, action.caster)
					break
				case '辅助':
					this.showHealAnimation(action.target, action.value)
					break
				case '控制':
					this.showSkillAnimation(action.caster, action.skill)
					break
			}
		},
		
		/**
		 * 显示技能动画
		 */
		showSkillAnimation(caster, skill) {
			const allUnits = [...this.allies, ...this.enemies]
			const unit = allUnits.find(u => u.id === caster.id)
			if (!unit) return
			// 暂存技能名与提示锁，待连线开始时与动画同步显示
			this.$set(unit, 'pendingSkillName', skill && skill.name ? String(skill.name) : '')
			this.$set(unit, 'skillPromptConsumed', false)
		},

		// 技能/BUFF提示队列（每单位独立，避免覆盖）——与冷却对齐
		enqueueSkillPrompt(unit, text, duration, readyAt) {
			const uid = String(unit.id)
			if (!this.skillPromptQueues[uid]) this.skillPromptQueues[uid] = []
			const resolvedReadyAt = typeof readyAt === 'number' ? readyAt : Math.max(Date.now(), this.skillCooldownUntil[uid] || 0)
			this.skillPromptQueues[uid].push({ text, duration, readyAt: resolvedReadyAt })
			this.processSkillPrompt(uid)
		},
		processSkillPrompt(uid) {
			if (this.isProcessingSkillPrompt[uid]) return
			// 暂停守卫：暂停时不启动新的技能提示
			if (this.battlePaused) { setTimeout(() => this.processSkillPrompt(uid), 100); return }
			const queue = this.skillPromptQueues[uid] || []
			const next = queue.shift()
			if (!next) return
			this.isProcessingSkillPrompt[uid] = true
			const allUnits = this.allies.concat(this.enemies)
			const unit = allUnits.find(u => String(u.id) === uid)
			if (!unit) { this.isProcessingSkillPrompt[uid] = false; return }
			const now = Date.now()
			const delay = Math.max(0, (next.readyAt || now) - now)
			const startPrompt = () => {
				if (this.battlePaused) { setTimeout(startPrompt, 100); return }
				unit.isUsingSkill = true
				unit.skillName = next.text
				const finishPrompt = () => {
					if (this.battlePaused) { setTimeout(finishPrompt, 100); return }
					unit.isUsingSkill = false
					unit.skillName = null
					this.isProcessingSkillPrompt[uid] = false
					this.processSkillPrompt(uid)
				}
				setTimeout(finishPrompt, next.duration)
			}
			if (delay > 0) setTimeout(startPrompt, delay)
			else startPrompt()
		},

		getSkillPromptDuration() {
			try {
				const container = this.$el
				if (container) {
					const val = window.getComputedStyle(container).getPropertyValue('--skill-duration').trim()
					if (val.endsWith('ms')) return parseFloat(val)
					if (val.endsWith('s')) return parseFloat(val) * 1000
				}
			} catch (e) {}
			// 回退到代码中的时长（全局减速一倍）
			return this.isSpeedUp ? 800 : 1600
		},
        // 新增：技能多目标连线（与日志同步）——改为队列，避免连续技能重叠，并与技能冷却对齐
        showSkillTargets(actorId, targetIds, actionType, options = {}) {
            try {
                const list = Array.isArray(targetIds) ? targetIds : (Array.isArray(arguments[1]) ? arguments[1] : [])
                const kind = typeof actionType === 'string' ? actionType : '攻击'
                const actorKey = typeof actorId === 'object' && actorId ? String(actorId.id) : String(actorId)
                const readyAt = Math.max(Date.now(), this.skillCooldownUntil[actorKey] || 0)
                // 添加技能类型信息用于颜色区分
                const skillType = options.skillType || kind
                this.skillTargetsQueue.push({ 
                    actorId, 
                    targetIds: list, 
                    actionType: kind, 
                    skillType: skillType,
                    readyAt 
                })
                this.processSkillTargetsQueue()
            } catch (e) {}
        },
        processSkillTargetsQueue() {
            if (this.isProcessingSkillTargets) return
            // 若攻击队列或多线动画在进行，稍后再处理，避免重叠
            if (this.isProcessingAttack || this.multiLineActive) {
                setTimeout(() => this.processSkillTargetsQueue(), 50)
                return
            }
            // 暂停守卫：暂停时不处理技能目标，延迟重试
            if (this.battlePaused) {
                setTimeout(() => this.processSkillTargetsQueue(), 100)
                return
            }
            const next = this.skillTargetsQueue.shift()
            if (!next) return
            this.isProcessingSkillTargets = true
            const now = Date.now()
            const delay = Math.max(0, (next.readyAt || now) - now)
            if (delay > 0) {
                setTimeout(() => this.renderSkillTargets(next), delay)
            } else {
                this.renderSkillTargets(next)
            }
        },
        renderSkillTargets({ actorId, targetIds, actionType, skillType, readyAt }) {
            try {
                const container = this.$el
                if (!container) { this.isProcessingSkillTargets = false; return }
                const containerRect = container.getBoundingClientRect()
                const resolveUnitById = (id) => {
                    const str = String(id || '')
                    const allUnits = [...this.allies, ...this.enemies]
                    return allUnits.find(u => String(u.id) === str) || null
                }
                const attackerUnit = typeof actorId === 'object' && actorId ? resolveUnitById(actorId.id) : resolveUnitById(actorId)
                if (!attackerUnit) { 
                    this.isProcessingSkillTargets = false
                    return 
                }
                const attackerEl = container.querySelector(`.unit[data-unit-id="${attackerUnit.id}"]`)
                if (!attackerEl) { 
                    this.isProcessingSkillTargets = false
                    return 
                }
                const attackerRect = attackerEl.getBoundingClientRect()
                const isAllyAttack = attackerUnit && (attackerUnit.isPlayer || attackerUnit.type === 'player')
                const lines = []
                ;(targetIds || []).forEach(t => {
                    const targetUnit = typeof t === 'object' && t ? resolveUnitById(t.id) : resolveUnitById(t)
                    if (!targetUnit) return
                    const targetEl = container.querySelector(`.unit[data-unit-id="${targetUnit.id}"]`)
                    if (!targetEl) return
                    const targetRect = targetEl.getBoundingClientRect()
                    let x1 = Math.round(attackerRect.left - containerRect.left + attackerRect.width / 2)
                    let y1 = Math.round(attackerRect.top - containerRect.top + attackerRect.height / 2)
                    let x2 = Math.round(targetRect.left - containerRect.left + targetRect.width / 2)
                    let y2 = Math.round(targetRect.top - containerRect.top + targetRect.height / 2)
                    // 修复水平连线渲染：增加更大的偏移量避免渲染问题
                    if (x1 === x2 && y1 !== y2) { x2 += 2 }
                    if (y1 === y2 && x1 !== x2) { y2 += 2 }
                    if (x1 === x2 && y1 === y2) { x2 += 2; y2 += 2 }
                    // 添加技能类型信息用于颜色区分
                    lines.push({ x1, y1, x2, y2, isAllyAttack, kind: actionType, skillType: skillType || actionType })
                })
                // 行动开始指示 & 预先设置冷却到动作结束时刻
                // 优化：直接使用引擎的 lineDuration，确保与引擎等待时间完全一致
                let lineDuration = 800 // 默认值
                try {
                    if (this.battleEngine && typeof this.battleEngine.getLineAnimationDuration === 'function') {
                        lineDuration = this.battleEngine.getLineAnimationDuration(actionType || '攻击')
                    } else {
                        lineDuration = this.getAttackDuration()
                    }
                } catch (e) {
                    lineDuration = this.getAttackDuration()
                }
                const attackDuration = lineDuration
                const damageDuration = this.getDamageDuration()
                const uid = String(attackerUnit.id)
                this.$set(attackerUnit, 'actionActive', true)
                this.skillCooldownUntil[uid] = Date.now() + attackDuration + damageDuration
                // 防重锁：若同一攻击者的连线仍在持续，跳过重复绘制与提示
                const nowTs = Date.now()
                const lockUntil = this.lineLockUntil[uid] || 0
                const locked = nowTs < lockUntil
                if (!locked) {
                    // 设置锁至连线结束
                    this.lineLockUntil[uid] = nowTs + lineDuration
                    // 连线显示
                    this.attackLines = lines
                    this.multiLineActive = lines.length > 0
                    // 与连线开始同步显示技能名称提示（加倍时长，确保只触发一次）
                    const pendingText = attackerUnit && attackerUnit.pendingSkillName
                    if (pendingText && attackerUnit && !attackerUnit.skillPromptConsumed) {
                        const promptDuration = lineDuration * 2
                        this.enqueueSkillPrompt(attackerUnit, pendingText, promptDuration, Date.now())
                        this.$set(attackerUnit, 'skillPromptConsumed', true)
                        this.$set(attackerUnit, 'pendingSkillName', null)
                    }
                }
                setTimeout(() => {
                    // 暂停守卫：暂停时保持连线与伤害等待，不推进到下一步
                    if (this.battlePaused) {
                        setTimeout(() => {
                            // 暂停时也使用轮询机制
                            const maxWaitTime = 200
                            const pollInterval = 10
                            const startTime = Date.now()
                            const pollCheck = () => {
                                const pendingCount = Object.keys(this.pendingMultiDamage || {}).length
                                const elapsed = Date.now() - startTime
                                if (pendingCount > 0 || elapsed >= maxWaitTime) {
                                    this.flushPendingMultiDamage()
                                    const hitDelay = (this.battleEngine && typeof this.battleEngine.getHitSyncDelayMs === 'function') ? this.battleEngine.getHitSyncDelayMs() : 30
                                    setTimeout(() => {
                                        this.attackLines = []
                                        this.multiLineActive = false
                                    }, hitDelay)
                                    setTimeout(() => {
                                        this.$set(attackerUnit, 'actionActive', false)
                                        this.isProcessingSkillTargets = false
                                        this.processSkillTargetsQueue()
                                    }, damageDuration)
                                } else {
                                    setTimeout(pollCheck, pollInterval)
                                }
                            }
                            pollCheck()
                         }, 100)
                         return
                     }
                     // 优化：使用轮询机制，一旦检测到 pendingMultiDamage 有数据就立即 flush
                     // 因为引擎的 setTimeout 可能在页面的 setTimeout 之后才执行（事件循环时序）
                     const maxWaitTime = 200 // 最多等待200ms
                     const pollInterval = 10 // 每10ms检查一次
                     const startTime = Date.now()
                     const pollCheck = () => {
                         const pendingCount = Object.keys(this.pendingMultiDamage || {}).length
                         const elapsed = Date.now() - startTime
                         if (pendingCount > 0 || elapsed >= maxWaitTime) {
                             this.flushPendingMultiDamage()
                             // 稍后再清除连线，避免"线未到伤害先出"的错觉
                             const hitDelay = (this.battleEngine && typeof this.battleEngine.getHitSyncDelayMs === 'function') ? this.battleEngine.getHitSyncDelayMs() : 30
                             setTimeout(() => {
                                 this.attackLines = []
                                 this.multiLineActive = false
                             }, hitDelay)
                             // 等待伤害数字消退后，再处理下一个技能目标，避免重叠
                             setTimeout(() => {
                                 // 行动完成指示
                                 this.$set(attackerUnit, 'actionActive', false)
                                 // 移除行动完成文字提示，仅保留边框颜色指示
                                 this.isProcessingSkillTargets = false
                                 this.processSkillTargetsQueue()
                             }, damageDuration)
                         } else {
                             setTimeout(pollCheck, pollInterval)
                         }
                     }
                     pollCheck()
                 }, lineDuration)
            } catch (e) {
                this.isProcessingSkillTargets = false
                this.processSkillTargetsQueue()
            }
        },
        flushPendingMultiDamage() {
            try {
                const allUnits = this.allies.concat(this.enemies)
                const keys = Object.keys(this.pendingMultiDamage || {})
                keys.forEach(key => {
                    const unit = allUnits.find(u => String(u.id) === key || `${u.name}${u.id}` === key || `怪物_${u.name}${u.id}` === key)
                    if (!unit) return
                    const { damage, isCrit } = this.pendingMultiDamage[key]
                    unit.damageText = damage
                    unit.damageKey = Date.now()
                    unit.isCrit = !!isCrit
                    const damageTextDuration = this.getDamageDuration()
                    setTimeout(() => {
                        unit.damageText = null
                        unit.isCrit = false
                    }, damageTextDuration)
                })
                this.pendingMultiDamage = {}
            } catch (e) {}
        },
		showDamageAnimation(target, damage, isCrit, attacker) {
            // 普通攻击和技能都应该统一处理：如果连线正在绘制或即将绘制，则缓冲伤害到连线到达时统一展示
            try {
                const attackerId = attacker && attacker.id ? String(attacker.id) : null
                
                // 检查是否有该攻击者的连线正在处理（包括队列中待处理的）
                const hasActiveLineForAttacker = !!attackerId && (
                    this.isProcessingSkillTargets ||  // 正在处理技能目标队列
                    this.multiLineActive ||  // 多线动画正在进行
                    (this.skillTargetsQueue || []).some(item => {  // 队列中有待处理的目标
                        const aid = typeof item.actorId === 'object' && item.actorId ? String(item.actorId.id) : String(item.actorId)
                        return aid === attackerId
                    }) ||
                    // 检查攻击队列中是否有该攻击者的连线待处理
                    (this.attackQueue || []).some(item => {
                        const aid = item && item.attacker ? String(item.attacker.id) : null
                        return aid === attackerId
                    }) ||
                    // 检查是否有该攻击者的连线锁定（说明连线正在或即将开始）
                    (this.lineLockUntil && this.lineLockUntil[attackerId] && Date.now() < this.lineLockUntil[attackerId])
                )
                
                if (hasActiveLineForAttacker) {
                    // 缓冲伤害，等连线到达时统一展示（与技能一致的处理方式）
                    const key = String(target && target.id)
                    if (!this.pendingMultiDamage) this.pendingMultiDamage = {}
                    this.pendingMultiDamage[key] = { damage, isCrit, attacker }
                    return
                }
            } catch (e) {}
            
            // 如果没有连线处理，立即显示伤害（兼容没有走onSkillTargets的旧逻辑）
            const allUnits = [...this.allies, ...this.enemies]
            const unit = allUnits.find(u => u && u.id === (target && target.id))
            if (unit) {
                unit.damageText = damage
                unit.damageKey = Date.now()
                unit.isCrit = !!isCrit
                const damageTextDuration = this.getDamageDuration()
                setTimeout(() => {
                    unit.damageText = null
                    unit.isCrit = false
                }, damageTextDuration)
            }
        },
		
		/**
		 * 显示治疗动画
		 */
		showHealAnimation(target, healAmount) {
			const allUnits = [...this.allies, ...this.enemies]
			const unit = allUnits.find(u => u.id === target.id)
			
			if (unit) {
				unit.healText = healAmount
				unit.healKey = Date.now()
				
				const healTextDuration = this.getHealDuration()
				setTimeout(() => {
					unit.healText = null
				}, healTextDuration)
			}
		},

		// 新增：Buff 弹窗显示（堆叠+与技能提示一致并提供行动阻断）
		showBuffPopup(targetUnit, buffName) {
			try {
				const allUnits = this.allies.concat(this.enemies)
				const unit = allUnits.find(u => u.id === (targetUnit && targetUnit.id))
				if (!unit) return
				const text = typeof buffName === 'string' ? buffName : (buffName && buffName.name ? String(buffName.name) : '获得状态')
				// 堆叠弹窗（独立展示，使用分隔线区分）
				if (!unit.buffPopups) this.$set(unit, 'buffPopups', [])
				const item = { text, key: Date.now() }
				unit.buffPopups.push(item)
				const popupDuration = this.getBuffPopupDuration()
				setTimeout(() => {
					const idx = unit.buffPopups.findIndex(x => x.key === item.key)
					if (idx !== -1) unit.buffPopups.splice(idx, 1)
				}, popupDuration)
				// 行动阻断并使用统一技能提示风格
				const promptDuration = this.getSkillPromptDuration()
				unit.buffActiveName = text
				unit.buffBlockUntil = Date.now() + promptDuration
				this.enqueueSkillPrompt(unit, text, promptDuration)
				setTimeout(() => { unit.buffActiveName = null; unit.buffBlockUntil = 0 }, promptDuration)
			} catch (e) {}
		},

		// 新增：控制抵抗提示（与技能名称视觉一致，旁侧显示）
		showResistHint(casterUnit, text = '被抵抗') {
			try {
				const allUnits = this.allies.concat(this.enemies)
				const unit = allUnits.find(u => u.id === (casterUnit && casterUnit.id))
				if (!unit) return
				const duration = this.getSkillPromptDuration()
				this.$set(unit, 'resistHint', text)
				setTimeout(() => {
					unit.resistHint = null
				}, duration)
			} catch (e) {}
		},

		// 新增：Buff 弹窗时长
		getBuffPopupDuration() {
			try {
				const container = this.$el
				if (container) {
					const val = window.getComputedStyle(container).getPropertyValue('--buff-duration').trim()
					if (val.endsWith('ms')) return parseFloat(val)
					if (val.endsWith('s')) return parseFloat(val) * 1000
				}
			} catch (e) {}
			return this.isSpeedUp ? 600 : 1200
		},

		/**
		 * 显示攻击连线
		 */
		showAttackLine(attacker, target) {
            // 兼容后端键：支持 id、name+id、怪物_前缀
            const container = this.$el
            const resolveKey = (key) => {
                const str = String(key || '')
                // 仅按唯一 id 解析
                const allUnits = [...this.allies, ...this.enemies]
                const hit = allUnits.find(u => String(u.id) === str)
                return hit ? String(hit.id) : null
            }
            const targetKey = resolveKey(target && target.id)
            const attackerKey = resolveKey(attacker && attacker.id)
            const targetEl = targetKey ? this.$el.querySelector(`.unit[data-unit-id="${targetKey}"]`) : null
            const attackerEl = attackerKey ? this.$el.querySelector(`.unit[data-unit-id="${attackerKey}"]`) : null
            if (!container || !targetEl || !attackerEl) {
                console.warn('[AttackLine] 元素缺失', {
                    attackerId: attacker && attacker.id,
                    targetId: target && target.id,
                    resolvedAttackerId: attackerKey,
                    resolvedTargetId: targetKey,
                    containerFound: !!container,
                    attackerFound: !!attackerEl,
                    targetFound: !!targetEl
                })
                return false
            }
            // 防重锁：若同一攻击者的连线仍在持续，跳过重复绘制
            const now = Date.now()
            const lockUntil = this.lineLockUntil[attackerKey] || 0
            if (now < lockUntil) {
                return false
            }
            const containerRect = container.getBoundingClientRect()
            const targetRect = targetEl.getBoundingClientRect()
            const attackerRect = attackerEl.getBoundingClientRect()
            let x2 = Math.round(targetRect.left - containerRect.left + targetRect.width / 2)
            let y2 = Math.round(targetRect.top - containerRect.top + targetRect.height / 2)
            let x1 = Math.round(attackerRect.left - containerRect.left + attackerRect.width / 2)
            let y1 = Math.round(attackerRect.top - containerRect.top + attackerRect.height / 2)
            // 修复水平连线渲染：增加更大的偏移量避免渲染问题
            if (x1 === x2 && y1 !== y2) { x2 += 2 }
            if (y1 === y2 && x1 !== x2) { y2 += 2 }
            if (x1 === x2 && y1 === y2) { x2 += 2; y2 += 2 }
            console.debug('[AttackLine] 绘线坐标', { x1, y1, x2, y2, isAllyAttack: attacker && (attacker.isPlayer || attacker.type === 'player') })
            this.attackLine = { x1, y1, x2, y2, isAllyAttack: attacker && (attacker.isPlayer || attacker.type === 'player') }
            // 设置防重锁至连线结束
            const attackDuration = this.getAttackDuration()
            const lineDuration = attackDuration
            this.lineLockUntil[attackerKey] = now + lineDuration
            return true
        },

		enqueueAttackLine(attacker, target, damage, isCrit) {
			this.attackQueue.push({ attacker, target, damage, isCrit })
			this.processAttackQueue()
		},

		processAttackQueue() {
			if (this.isProcessingAttack) return
			// 若技能目标渲染或多线动画在进行，稍后再处理攻击，避免重叠
			if (this.isProcessingSkillTargets || this.multiLineActive) {
				setTimeout(() => this.processAttackQueue(), 50)
				return
			}
			// 暂停守卫：暂停时不处理攻击队列
			if (this.battlePaused) {
				setTimeout(() => this.processAttackQueue(), 100)
				return
			}
			const next = this.attackQueue.shift()
			if (!next) return
			this.isProcessingAttack = true
			// 注意：伤害数字已在 showDamageAnimation 中立即显示，这里只负责连线动画
			this.$nextTick(() => {
				const hadLine = this.showAttackLine(next.attacker, next.target)
				const attackDuration = this.getAttackDuration()
				const lineDuration = attackDuration
				// 与连线开始同步显示技能名称提示（加倍时长）
				try {
					const allUnits = this.allies.concat(this.enemies)
					const aKey = next && next.attacker ? String(next.attacker.id) : ''
					const attackerUnit = allUnits.find(u => String(u.id) === aKey || `${u.name}${u.id}` === aKey || `怪物_${u.name}${u.id}` === aKey)
					const pendingText = attackerUnit && attackerUnit.pendingSkillName
					if (pendingText && attackerUnit && !attackerUnit.skillPromptConsumed && hadLine) {
						this.enqueueSkillPrompt(attackerUnit, pendingText, lineDuration, Date.now())
						this.$set(attackerUnit, 'skillPromptConsumed', true)
						this.$set(attackerUnit, 'pendingSkillName', null)
					}
				} catch (e) {}
				if (hadLine) {
					setTimeout(() => {
						// 暂停守卫：暂停时保持连线，延后清理
						if (this.battlePaused) { 
							setTimeout(() => {
								// 稍后再清线
								const hitDelay = (this.battleEngine && typeof this.battleEngine.getHitSyncDelayMs === 'function') ? this.battleEngine.getHitSyncDelayMs() : 30
								setTimeout(() => { this.attackLine = null }, hitDelay)
								this.isProcessingAttack = false
								this.processAttackQueue()
							}, 100)
							return 
						}
						// 连线到达时清线（伤害数字已在showDamageAnimation中立即显示）
						const hitDelay = (this.battleEngine && typeof this.battleEngine.getHitSyncDelayMs === 'function') ? this.battleEngine.getHitSyncDelayMs() : 30
						setTimeout(() => { 
							this.attackLine = null
							this.isProcessingAttack = false
							this.processAttackQueue()
						}, hitDelay)
					}, lineDuration)
				} else {
					// 未绘线时直接完成
					this.isProcessingAttack = false
					this.processAttackQueue()
				}
			})
		},

		getAttackDuration() {
			try {
				const container = this.$el
				if (container) {
					const val = window.getComputedStyle(container).getPropertyValue('--attack-duration').trim()
					if (val.endsWith('ms')) return parseFloat(val)
					if (val.endsWith('s')) return parseFloat(val) * 1000
				}
			} catch (e) {}
			// 回退到代码中的时长（全局减速一倍）
			return this.isSpeedUp ? 300 : 600
		},

		getDamageDuration() {
			try {
				const container = this.$el
				if (container) {
					const val = window.getComputedStyle(container).getPropertyValue('--damage-duration').trim()
					if (val.endsWith('ms')) return parseFloat(val)
					if (val.endsWith('s')) return parseFloat(val) * 1000
				}
			} catch (e) {}
			return this.isSpeedUp ? 600 : 1200
		},

		getHealDuration() {
			try {
				const container = this.$el
				if (container) {
					const val = window.getComputedStyle(container).getPropertyValue('--heal-duration').trim()
					if (val.endsWith('ms')) return parseFloat(val)
					if (val.endsWith('s')) return parseFloat(val) * 1000
				}
			} catch (e) {}
			return this.isSpeedUp ? 600 : 1200
		},
		
		/**
		 * 结束战斗
		 */
		async endBattle() {
			if (this.battleTimer) {
				clearInterval(this.battleTimer)
				this.battleTimer = null
			}
			
			const battleState = this.battleEngine.getBattleState()
			const victory = battleState.winner === 'player'
			
			// 提交战斗结果到后端
			this.isLoading = true
			const result = await this.battleEngine.submitBattleResult()
			this.isLoading = false
			
			this.battleResult = {
				victory: victory,
				message: victory ? '恭喜你取得了胜利！' : '很遗憾，你失败了...',
				rewards: result ? result.rewards : null,
				experience: result ? result.experience : 0
			}
			
			this.addLog(victory ? '战斗胜利！' : '战斗失败！')
		},
		
		/**
		 * 重新开始战斗
		 */
	async restartBattle() {
			this.battleResult = null
			this.battleLogs = []
			this.attackLine = null
			this.attackQueue = []
			this.isProcessingAttack = false
			
			if (this.battleTimer) {
				clearInterval(this.battleTimer)
				this.battleTimer = null
			}
			
			// 重置战斗引擎
			this.battleEngine.reset()
			
			// 重新初始化战斗
			await this.initializeBattle()
		},
		
		/**
		 * 添加日志
		 */
		addLog(message) {
			this.battleLogs.push(message)

			// 同时在控制台输出
			console.log('[战斗日志]', message)

			// 限制日志数量
			if (this.battleLogs.length > 50) {
				this.battleLogs = this.battleLogs.slice(-50)
			}

			// 如果弹窗未打开，增加新日志计数
			if (!this.showLogModal) {
				this.newLogCount++
			}

			// 自动滚动到底部（仅在弹窗打开时）
			if (this.showLogModal) {
				this.$nextTick(() => {
					const logContent = document.querySelector('.log-modal-content')
					if (logContent) {
						logContent.scrollTop = logContent.scrollHeight
					}
				})
			}
		},

		toggleLogModal() {
			this.showLogModal = !this.showLogModal
			if (this.showLogModal) {
				this.newLogCount = 0
			}
		},

		closeLogModal() {
			this.showLogModal = false
			this.newLogCount = 0
		},

		/**
		 * 显示当前行动日志
		 */
		showCurrentAction(actionData) {
			// 在每次新行动开始前，清理上一动作的瞬时覆盖层，避免叠盖
			try {
				this.clearTransientOverlays()
			} catch (e) {}
			if (!actionData) {
				// 不再清空文案，保持上一条显示
				return
			}

			// 解析行动数据
			const actorId = actionData.actorId
			const allUnits = [...this.allies, ...this.enemies]

			// 处理回合开始/结束
			if (actorId === 'round_start') {
				this.currentActionLog = {
					isRoundStart: true,
					round: actionData.round,
					description: actionData.description || `第${actionData.round}回合开始，处理辅助，控制状态`
				}
				// 回合开始提示保持显示，不自动清空
				return
			}

			if (actorId === 'round_end') {
				this.currentActionLog = {
					isRoundEnd: true,
					round: actionData.round,
					description: actionData.description || `第${actionData.round}回合结束`
				}
				// 回合结束提示保持显示，不自动清空
				return
			}

			// 查找行动者
			const actor = allUnits.find(u => u && u.id === actorId)
			if (!actor) return

			// 判断行动类型
			let actionType = '行动'
			let skillName = ''
			let description = actionData.description || ''

			if (actionData.normal_attack) {
				actionType = '攻击'
				description = actionData.normal_attack.description || actionData.description
			} else if (actionData.battleSkillEffect && actionData.battleSkillEffect.length > 0) {
				const skill = actionData.battleSkillEffect[0]
				actionType = skill.actionType || '技能'
				skillName = skill.skillName || ''
				description = skill.description || actionData.description
			} else if (actionData.buffName) {
				actionType = 'BUFF'
				skillName = actionData.buffName
				description = actionData.description
			}

			this.currentActionLog = {
				actorName: actor.name,
				actionType: actionType,
				skillName: skillName,
				description: description
			}

			// 不再自动清空，文案会持续显示直到下一个行动更新
		},

		// 清理单位的瞬时覆盖层（伤害/治疗数字、抵抗提示、临时buff弹窗）
		clearTransientOverlays() {
			try {
				const allUnits = (this.allies || []).concat(this.enemies || [])
				allUnits.forEach(u => {
					if (!u) return
					// 伤害/暴击
					u.damageText = null
					u.isCrit = false
					// 治疗
					u.healText = null
					// 抵抗提示
					u.resistHint = null
					// 临时buff弹窗堆叠
					if (Array.isArray(u.buffPopups)) u.buffPopups.splice(0, u.buffPopups.length)
					// 正在显示的技能名提示
					u.skillName = null
					u.isUsingSkill = false
				})
			} catch (e) {}
		},

		/** 键盘 ESC 关闭属性弹窗 */
		onKeydownEsc(e) {
			try {
				if (e && e.key === 'Escape' && this.showAttrModal) {
					this.closeAttrModal()
				}
			} catch (_) {}
		},
		
		/**
		 * 返回上一页
		 */
		goBack() {
			// 清理定时器
			if (this.battleTimer) {
				clearInterval(this.battleTimer)
				this.battleTimer = null
			}
			
			// 使用uni-app的导航方法
			if (typeof uni !== 'undefined' && uni.navigateBack) {
				uni.navigateBack()
			} else {
				// 备用方案：使用浏览器历史记录
				if (window.history.length > 1) {
					window.history.back()
				} else {
					// 如果没有历史记录，跳转到主页
					window.location.href = '/#/pages/main/index'
				}
			}
		},
		
		// 获取技能连线颜色
		getSkillLineColor(line) {
			const skillType = line.skillType || line.kind || '攻击'
			const isAlly = line.isAllyAttack
			
			if (skillType === '攻击') {
				return isAlly ? 'url(#allyAttackStroke)' : 'url(#enemyAttackStroke)'
			} else if (skillType === '辅助') {
				return isAlly ? 'url(#allySupportStroke)' : 'url(#enemySupportStroke)'
			} else if (skillType === '控制') {
				return isAlly ? 'url(#allyControlStroke)' : 'url(#enemyControlStroke)'
			} else {
				// 普通攻击或其他类型
				return isAlly ? 'url(#allyNormalStroke)' : 'url(#enemyNormalStroke)'
			}
		},
		
		// 获取技能连线宽度
		getSkillLineWidth(line) {
			const skillType = line.skillType || line.kind || '攻击'
			switch (skillType) {
				case '攻击': return 4  // 攻击技能最粗
				case '控制': return 3  // 控制技能中等
				case '辅助': return 2  // 辅助技能较细
				default: return 3      // 普通攻击中等
			}
		},
		
		// 获取技能连线虚线样式
		getSkillLineDash(line) {
			// 优化：所有类型统一使用实线，用颜色区分
			return null
		}
	}
}
</script>

<style scoped>
/* ========== 武侠风格战斗页面 ========== */
.battle-container {
	width: 100%;
	height: 100vh;
	height: calc(100vh - env(safe-area-inset-top) - env(safe-area-inset-bottom));
	max-height: 100vh;
	max-height: calc(100vh - env(safe-area-inset-top) - env(safe-area-inset-bottom));
	background: linear-gradient(180deg, #e8dcc4 0%, #d4c4a8 100%);
	padding: 4px 6px; /* 减少左右padding */
	padding-top: calc(4px + env(safe-area-inset-top, 0px));
	padding-bottom: calc(4px + env(safe-area-inset-bottom, 0px));
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	overflow: hidden;
	display: flex;
	flex-direction: column;
	font-family: "STSong", "Noto Serif SC", serif;
	--buff-duration: 1200ms;
}

.battle-header {
	display: flex;
	justify-content: space-between;
	align-items: center;
	background: rgba(255, 255, 255, 0.95);
	border: 2px solid #c4a57b;
	border-radius: 8px;
	padding: 6px 10px; /* 减少padding */
	margin-bottom: 4px; /* 减少底部间距 */
	box-shadow: 0 3px 10px rgba(139, 111, 71, 0.3);
	flex-shrink: 0; /* 防止压缩 */
}

.battle-title {
	font-size: 14px;
	font-weight: bold;
	color: #4a3c2a;
	letter-spacing: 1px;
}

.round-display {
	font-size: 12px;
	font-weight: bold;
	color: #c94a3f;
	background: rgba(201, 74, 63, 0.1);
	padding: 4px 10px;
	border-radius: 12px;
	border: 1px solid rgba(201, 74, 63, 0.3);
	white-space: nowrap;
}

.battle-controls {
	display: flex;
	gap: 6px;
	align-items: center;
	flex-wrap: wrap;
}

.speed-btn, .back-btn, .restart-btn {
	padding: 4px 8px;
	border: none;
	border-radius: 4px;
	font-size: 11px;
	cursor: pointer;
	transition: all 0.3s;
	white-space: nowrap;
}

.speed-btn {
	background: linear-gradient(135deg, #6b8e67 0%, #5a7a56 100%);
	color: white;
	position: relative;
	overflow: hidden;
	transition: all 0.3s ease;
	box-shadow: 0 2px 8px rgba(107, 142, 103, 0.3);
	border: 2px solid #4a6a46;
}

.speed-btn:hover {
	transform: translateY(-1px);
	box-shadow: 0 4px 12px rgba(52, 152, 219, 0.4);
}

.speed-btn.active {
	background: linear-gradient(135deg, #c94a3f 0%, #a83931 100%);
	box-shadow: 0 2px 8px rgba(201, 74, 63, 0.3);
	animation: speedPulse 1s infinite;
	border: 2px solid #9b3028;
}

.speed-btn.active:hover {
	box-shadow: 0 4px 12px rgba(231, 76, 60, 0.4);
}

@keyframes speedPulse {
	0%, 100% {
		transform: scale(1);
	}
	50% {
		transform: scale(1.05);
	}
}

.back-btn {
	background: linear-gradient(135deg, #8b6f47 0%, #6b4f3b 100%);
	color: white;
	border: 2px solid #4b3a2a;
}

.restart-btn {
	background: linear-gradient(135deg, #6b8e67 0%, #5a7a56 100%);
	color: white;
	margin-right: 10px;
	border: 2px solid #4a6a46;
}

.enemy-area, .player-area {
	background: rgba(255, 255, 255, 0.95);
	border: 2px solid #c4a57b;
	border-radius: 8px;
	padding: 4px 6px; /* 减少padding，让卡片占据更多空间 */
	margin-bottom: 3px; /* 减少区域间距 */
	box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
	flex: 1 1 auto; /* 允许flex分配空间 */
	max-height: 42%; /* 限制最大高度，为日志区域腾出空间 */
	min-height: 0; /* 允许压缩 */
	display: flex;
	flex-direction: column;
	overflow: visible; /* 允许内容溢出显示 */
}

.enemy-area {
	border-left: 4px solid #e74c3c;
}

.player-area {
	border-left: 4px solid #27ae60;
}

/* 实时行动日志展示区 */
.action-log-display {
	position: relative;
	margin: 4px auto;
	padding: 10px 14px;
	background: linear-gradient(135deg, rgba(255, 255, 255, 0.98) 0%, rgba(245, 245, 245, 0.98) 100%);
	border-radius: 8px;
	box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.9);
	border: 2px solid rgba(212, 175, 55, 0.3);
	width: 100%;
	backdrop-filter: blur(5px);
	/* 固定高度，防止跳动 */
	height: 110px; /* 固定高度 */
	min-height: 110px;
	max-height: 110px;
	display: flex;
	align-items: center;
	gap: 10px;
	flex-shrink: 0;
	overflow: hidden;
}

.action-log-content {
	flex: 1;
	text-align: left;
	font-family: '华文行楷', 'STXingkai', 'KaiTi', serif;
	animation: actionLogSlide 0.4s ease-out;
	min-width: 0; /* 允许flex压缩 */
	overflow: hidden;
}

.action-log-placeholder {
	flex: 1;
	text-align: left;
	font-size: 16px;
	color: #999;
	font-family: '微软雅黑', 'Microsoft YaHei', sans-serif;
}

@keyframes actionLogSlide {
	from {
		opacity: 0;
		transform: translateY(-10px) scale(0.98);
	}
	to {
		opacity: 1;
		transform: translateY(0) scale(1);
	}
}

.action-round-info {
	font-size: 20px;
	font-weight: bold;
	color: #2c3e50;
	text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
	padding: 4px 0;
	background: linear-gradient(135deg, #d4af37 0%, #c9a227 100%);
	-webkit-background-clip: text;
	-webkit-text-fill-color: transparent;
	background-clip: text;
	margin-bottom: 4px;
}

.action-actor-info {
	display: flex;
	align-items: center;
	justify-content: flex-start;
	gap: 10px;
	margin-bottom: 6px;
	flex-wrap: wrap;
}

.actor-name {
	font-size: 18px;
	font-weight: bold;
	color: #2c3e50;
	padding: 4px 10px;
	background: rgba(212, 175, 55, 0.15);
	border-radius: 6px;
	border: 1px solid rgba(212, 175, 55, 0.4);
}

.action-type {
	font-size: 16px;
	font-weight: 600;
	padding: 4px 10px;
	border-radius: 6px;
	color: white;
	text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
}

.action-type.type-攻击 {
	background: linear-gradient(135deg, #dc143c 0%, #b22222 100%);
	box-shadow: 0 2px 8px rgba(220, 20, 60, 0.4);
}

.action-type.type-辅助 {
	background: linear-gradient(135deg, #32cd32 0%, #228b22 100%);
	box-shadow: 0 2px 8px rgba(50, 205, 50, 0.4);
}

.action-type.type-控制 {
	background: linear-gradient(135deg, #8a2be2 0%, #663399 100%);
	box-shadow: 0 2px 8px rgba(138, 43, 226, 0.4);
}

.action-skill {
	font-size: 17px;
	font-weight: 600;
	color: #d4af37;
	padding: 4px 10px;
	background: rgba(212, 175, 55, 0.2);
	border-radius: 6px;
	border: 1px solid rgba(212, 175, 55, 0.5);
	text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
}

.action-description {
	font-size: 16px;
	color: #333;
	line-height: 1.5;
	padding: 4px 0;
	font-family: '微软雅黑', 'Microsoft YaHei', sans-serif;
	font-weight: 500;
}

.area-title {
	font-size: 12px;
	font-weight: bold;
	color: #333;
	margin-bottom: 2px; /* 进一步减少标题底部间距 */
	text-align: center;
	flex-shrink: 0;
	padding: 1px 0; /* 减少标题padding */
}

.units-container {
	display: flex;
	flex-direction: column;
	gap: 3px; /* 保持小间距即可 */
	overflow: visible; /* 允许卡片溢出显示 */
	height: 100%;
	width: 100%;
	flex: 1; /* 占据更多空间 */
}

	.unit-row {
		display: flex;
		justify-content: space-between;
		align-items: stretch; /* 让卡片垂直拉伸填满 */
		gap: 8px;
		flex-wrap: nowrap;
		flex: 1 1 auto; /* 占据所有可用空间 */
		min-height: 0; /* 允许flex子元素缩放 */
		width: 100%;
	}

.front-row {
	max-width: unset;
	margin: 0 auto;
}

.back-row {
	max-width: unset;
	margin: 0 auto;
}

.unit {
	flex: 1 1 calc((100% - 16px) / 3); /* 3列布局，每列平均分配，减去gap */
	min-width: 0;
	max-width: calc((100% - 16px) / 3);
	/* 让卡片占满可用空间 */
	height: 100%; /* 占满父容器高度 */
	min-height: 140px; /* 大幅提高最小高度 */
	border-radius: 8px;
	padding: 14px 6px; /* 增加上下padding */
	display: flex;
	flex-direction: column;
	justify-content: space-around; /* 改为space-around，让元素垂直分布更均匀 */
	position: relative;
	transition: all 0.3s;
	animation: unitAppear 0.5s ease-out;
	box-sizing: border-box;
}

@keyframes unitAppear {
	from {
		opacity: 0;
		transform: scale(0.8);
	}
	to {
		opacity: 1;
		transform: scale(1);
	}
}

/* 武侠风格受击特效 */
.unit.hit-damage {
	animation: wuxiaHitDamage 0.6s ease-out;
}

.unit.hit-heal {
	animation: wuxiaHitHeal 0.6s ease-out;
}

.unit.hit-control {
	animation: wuxiaHitControl 0.8s ease-out;
}

@keyframes wuxiaHitDamage {
	0% {
		transform: scale(1);
		box-shadow: 0 0 0 rgba(220, 20, 60, 0);
	}
	20% {
		transform: scale(1.05) rotate(1deg);
		box-shadow: 0 0 20px rgba(220, 20, 60, 0.8), inset 0 0 15px rgba(255, 0, 0, 0.3);
		filter: brightness(1.3) contrast(1.2);
	}
	40% {
		transform: scale(0.98) rotate(-0.5deg);
		box-shadow: 0 0 15px rgba(220, 20, 60, 0.6);
	}
	100% {
		transform: scale(1) rotate(0deg);
		box-shadow: 0 0 0 rgba(220, 20, 60, 0);
		filter: brightness(1) contrast(1);
	}
}

@keyframes wuxiaHitHeal {
	0% {
		transform: scale(1);
		box-shadow: 0 0 0 rgba(50, 205, 50, 0);
	}
	30% {
		transform: scale(1.08);
		box-shadow: 0 0 25px rgba(50, 205, 50, 0.9), inset 0 0 20px rgba(0, 255, 0, 0.2);
		filter: brightness(1.4) saturate(1.3);
	}
	100% {
		transform: scale(1);
		box-shadow: 0 0 0 rgba(50, 205, 50, 0);
		filter: brightness(1) saturate(1);
	}
}

@keyframes wuxiaHitControl {
	0% {
		transform: scale(1) rotate(0deg);
		box-shadow: 0 0 0 rgba(138, 43, 226, 0);
	}
	25% {
		transform: scale(1.03) rotate(2deg);
		box-shadow: 0 0 18px rgba(138, 43, 226, 0.7), inset 0 0 12px rgba(128, 0, 128, 0.3);
		filter: hue-rotate(30deg) brightness(1.2);
	}
	50% {
		transform: scale(1.01) rotate(-1deg);
		box-shadow: 0 0 15px rgba(138, 43, 226, 0.5);
	}
	75% {
		transform: scale(1.02) rotate(1deg);
		box-shadow: 0 0 12px rgba(138, 43, 226, 0.4);
	}
	100% {
		transform: scale(1) rotate(0deg);
		box-shadow: 0 0 0 rgba(138, 43, 226, 0);
		filter: hue-rotate(0deg) brightness(1);
	}
}

.enemy-unit {
	background: linear-gradient(135deg, #ff7675 0%, #fd79a8 100%);
	color: white;
}

.ally-unit {
	background: linear-gradient(135deg, #00b894 0%, #00cec9 100%);
	color: white;
}

.unit.placeholder {
	background: rgba(255, 255, 255, 0.6);
	border: 1px dashed rgba(0, 0, 0, 0.15);
	box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.05);
}

.unit.defeated {
	opacity: 0.3;
	filter: grayscale(100%);
	transform: scale(0.9);
}

.unit-name {
	font-size: 13px; /* 增加字体 */
	font-weight: bold;
	text-align: center;
	line-height: 1.3; /* 增加行高 */
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	margin: 3px 0; /* 增加上下间距 */
}

.unit-hp {
	display: flex;
	flex-direction: column;
	gap: 4px; /* 增加间距 */
	flex-shrink: 0;
	margin: 3px 0; /* 增加上下间距 */
}

.hp-bar {
	height: 6px; /* 增加血条高度 */
	background: rgba(255, 255, 255, 0.3);
	border-radius: 3px;
	overflow: hidden;
}

.hp-fill {
	height: 100%;
	background: #ffffff;
	border-radius: 3px;
	transition: width 0.5s;
}

.hp-text {
	font-size: 10px; /* 增加字体 */
	text-align: center;
	line-height: 1.2;
	font-weight: 600;
	margin-top: 1px;
}

.unit.using-skill {
	/* 移除所有闪动效果 */
}

@keyframes skillGlow {
	from {
		box-shadow: 0 0 20px rgba(255, 255, 255, 0.8);
	}
	to {
		box-shadow: 0 0 30px rgba(255, 255, 255, 1);
	}
}

.damage-text.crit-damage {
	color: #ff6b35;
	font-size: 20px;
	text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8), 0 0 15px rgba(255, 107, 53, 0.9);
	animation: wuxiaCritFloat var(--damage-duration, 2s) ease-out;
}

@keyframes wuxiaCritFloat {
	0% {
		opacity: 0;
		transform: translateX(-50%) translateY(8px) scale(0.6) rotate(-5deg);
		filter: blur(3px);
	}
	15% {
		opacity: 1;
		transform: translateX(-50%) translateY(0) scale(1.4) rotate(2deg);
		filter: blur(0);
		text-shadow: 3px 3px 6px rgba(0, 0, 0, 0.9), 0 0 20px rgba(255, 107, 53, 1);
	}
	30% {
		transform: translateX(-50%) translateY(-5px) scale(1.3) rotate(-1deg);
	}
	70% {
		opacity: 0.9;
		transform: translateX(-50%) translateY(-25px) scale(1.1) rotate(0deg);
	}
	100% {
		opacity: 0;
		transform: translateX(-50%) translateY(-50px) scale(0.8) rotate(1deg);
		filter: blur(2px);
	}
}

@keyframes critFloat {
	from {
		opacity: 1;
		transform: translateX(-50%) translateY(0) scale(1);
	}
	50% {
		transform: translateX(-50%) translateY(-15px) scale(1.2);
	}
	to {
		opacity: 0;
		transform: translateX(-50%) translateY(-30px) scale(1);
	}
}

.heal-text {
	position: absolute;
	top: -25px;
	left: 50%;
	transform: translateX(-50%);
	color: #32cd32;
	font-weight: bold;
	font-size: 16px;
	font-family: '华文行楷', 'STXingkai', 'KaiTi', serif;
	text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8), 0 0 8px rgba(50, 205, 50, 0.6);
	animation: wuxiaHealFloat var(--heal-duration, 2s) ease-out;
	pointer-events: none;
	z-index: 50;
}

@keyframes wuxiaHealFloat {
	0% {
		opacity: 0;
		transform: translateX(-50%) translateY(5px) scale(0.8);
		filter: blur(2px);
	}
	20% {
		opacity: 1;
		transform: translateX(-50%) translateY(0) scale(1.1);
		filter: blur(0);
		text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8), 0 0 12px rgba(50, 205, 50, 0.8);
	}
	80% {
		opacity: 0.8;
		transform: translateX(-50%) translateY(-20px) scale(1);
	}
	100% {
		opacity: 0;
		transform: translateX(-50%) translateY(-35px) scale(0.9);
		filter: blur(1px);
	}
}

@keyframes healFloat {
	from {
		opacity: 1;
		transform: translateX(-50%) translateY(0);
	}
	to {
		opacity: 0;
		transform: translateX(-50%) translateY(-30px);
	}
}

.skill-name {
	position: absolute;
	top: -50px;
	left: 50%;
	transform: translateX(-50%);
	color: #d4af37;
	font-weight: bold;
	font-size: 14px;
	font-family: '华文行楷', 'STXingkai', 'KaiTi', serif;
	background: linear-gradient(135deg, rgba(212, 175, 55, 0.9) 0%, rgba(184, 134, 11, 0.9) 100%);
	padding: 6px 12px;
	border-radius: 20px;
	border: 2px solid #d4af37;
	box-shadow: 0 0 15px rgba(212, 175, 55, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.3);
	text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
	animation: wuxiaSkillFloat var(--skill-duration, 3s) ease-out;
	pointer-events: none;
	z-index: 100;
}

@keyframes wuxiaSkillFloat {
	0% {
		opacity: 0;
		transform: translateX(-50%) translateY(10px) scale(0.8);
		filter: blur(2px);
	}
	20% {
		opacity: 1;
		transform: translateX(-50%) translateY(0) scale(1.1);
		filter: blur(0);
		box-shadow: 0 0 25px rgba(212, 175, 55, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.5);
	}
	80% {
		opacity: 1;
		transform: translateX(-50%) translateY(-5px) scale(1);
		filter: blur(0);
	}
	100% {
		opacity: 0;
		transform: translateX(-50%) translateY(-30px) scale(0.9);
		filter: blur(1px);
	}
}

.skill-name .skill-name-text { margin-right: 6px; }
.skill-name .resist-hint {
  color: #e74c3c;
  border-left: 1px solid rgba(255,255,255,0.3);
  padding-left: 6px;
}
.unit .resist-hint {
  position: absolute;
  top: -18px;
  left: 50%;
  transform: translateX(-50%);
  color: #e74c3c;
  background: rgba(231, 76, 60, 0.15);
  border: 1px solid rgba(231, 76, 60, 0.5);
  border-radius: 6px;
  padding: 2px 8px;
  font-size: 12px;
  font-weight: 600;
  box-shadow: 0 2px 8px rgba(231, 76, 60, 0.25);
  pointer-events: none;
}
.buff-stack {
  position: absolute;
  top: -28px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  pointer-events: none;
}
.buff-stack .buff-item {
  display: flex;
  align-items: center;
}
.buff-stack .buff-divider {
  width: 28px;
  height: 1px;
  background: rgba(255,255,255,0.6);
  margin: 3px 0;
}
.buff-stack .buff-popup {
  position: static;
  box-shadow: none;
  margin: 0;
}

.damage-text {
	position: absolute;
	top: -25px;
	left: 50%;
	transform: translateX(-50%);
	color: #dc143c;
	font-weight: bold;
	font-size: 16px;
	font-family: '华文行楷', 'STXingkai', 'KaiTi', serif;
	text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8), 0 0 8px rgba(220, 20, 60, 0.6);
	animation: wuxiaDamageFloat var(--damage-duration, 2s) ease-out;
	pointer-events: none;
	z-index: 50;
}

@keyframes wuxiaDamageFloat {
	0% {
		opacity: 0;
		transform: translateX(-50%) translateY(5px) scale(0.8);
		filter: blur(2px);
	}
	20% {
		opacity: 1;
		transform: translateX(-50%) translateY(0) scale(1.2);
		filter: blur(0);
		text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8), 0 0 12px rgba(220, 20, 60, 0.8);
	}
	80% {
		opacity: 0.8;
		transform: translateX(-50%) translateY(-20px) scale(1);
	}
	100% {
		opacity: 0;
		transform: translateX(-50%) translateY(-40px) scale(0.9);
		filter: blur(1px);
	}
}

/* 行动指示样式（移除闪动效果） */
.unit.actioning {
	/* 移除所有闪动效果 */
}

/* ========== 战斗播报按钮（紧凑版，放在中间区域右侧）========== */
.battle-log-button-wrapper {
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
}

.log-toggle-btn-compact {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 4px;
	padding: 8px 12px;
	background: linear-gradient(135deg, #8b6f47 0%, #6b4f3b 100%);
	border: 2px solid #4b3a2a;
	border-radius: 8px;
	color: white;
	font-size: 12px;
	font-weight: 600;
	cursor: pointer;
	transition: all 0.3s ease;
	box-shadow: 0 2px 8px rgba(75, 58, 42, 0.3);
	position: relative;
	min-width: 44px;
	height: 36px;
}

.log-toggle-btn-compact:hover {
	transform: translateY(-1px);
	box-shadow: 0 4px 12px rgba(75, 58, 42, 0.4);
}

.log-toggle-btn-compact .log-icon {
	font-size: 16px;
}

.log-badge-compact {
	position: absolute;
	top: -4px;
	right: -4px;
	background: linear-gradient(135deg, #c94a3f 0%, #a83931 100%);
	color: white;
	font-size: 11px;
	font-weight: 700;
	padding: 2px 6px;
	border-radius: 10px;
	border: 2px solid white;
	min-width: 18px;
	text-align: center;
}

/* ========== 战斗日志弹窗 ========== */
.log-modal-backdrop {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background: rgba(75, 58, 42, 0.6);
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 2000;
	backdrop-filter: blur(4px);
}

.log-modal {
	width: 90%;
	max-width: 500px;
	max-height: 70vh;
	background: rgba(248, 246, 241, 0.98);
	border: 4px solid #8b6f47;
	border-radius: 15px;
	box-shadow: 0 10px 40px rgba(139, 111, 71, 0.5);
	display: flex;
	flex-direction: column;
	overflow: hidden;
}

.log-modal-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 15px 20px;
	background: linear-gradient(135deg, #8b6f47 0%, #6b4f3b 100%);
	color: white;
	border-bottom: 2px solid #4b3a2a;
}

.modal-title {
	font-size: 18px;
	font-weight: 700;
	letter-spacing: 1px;
}

.modal-close {
	width: 30px;
	height: 30px;
	border: 2px solid rgba(255, 255, 255, 0.3);
	background: rgba(255, 255, 255, 0.2);
	color: white;
	border-radius: 50%;
	font-size: 20px;
	cursor: pointer;
	transition: all 0.3s ease;
}

.modal-close:hover {
	background: rgba(255, 255, 255, 0.3);
	border-color: rgba(255, 255, 255, 0.5);
}

.log-modal-content {
	flex: 1;
	overflow-y: auto;
	padding: 15px;
	background: rgba(255, 255, 255, 0.5);
}

.log-item {
	padding: 8px 12px;
	margin-bottom: 8px;
	font-size: 13px;
	color: #4a3c2a;
	background: rgba(255, 255, 255, 0.9);
	border-left: 3px solid #8b6f47;
	border-radius: 6px;
	line-height: 1.5;
}

.log-empty {
	text-align: center;
	padding: 40px 20px;
	color: #8b6f47;
	font-size: 14px;
	font-weight: 600;
}

.attack-lines {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	pointer-events: none;
	z-index: 10;
}

.attack-line {
	animation: none;
	filter: none;
}

.battle-result-modal {
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	background: rgba(0, 0, 0, 0.8);
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 1000;
}

.result-content {
	background: white;
	border-radius: 15px;
	padding: 30px;
	text-align: center;
	max-width: 300px;
	width: 90%;
	box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}

.result-content h2 {
	margin: 0 0 15px 0;
	font-size: 24px;
}

.result-content h2.victory {
	color: #27ae60;
}

.result-content h2.defeat {
	color: #e74c3c;
}

.result-content p {
	margin: 0 0 20px 0;
	color: #666;
	font-size: 16px;
}

.buff-popup {
	position: absolute;
	top: -28px;
	left: 50%;
	transform: translateX(-50%);
	background: rgba(255, 255, 255, 0.85);
	backdrop-filter: blur(2px);
	color: #2c3e50;
	font-weight: 600;
	font-size: 10px;
	padding: 3px 6px;
	border-radius: 6px;
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
	pointer-events: none;
	animation: popupRise var(--buff-duration, 1200ms) ease-out;
}

@keyframes popupRise {
	from {
		opacity: 1;
		transform: translateX(-50%) translateY(0);
	}
	to {
		opacity: 0;
		transform: translateX(-50%) translateY(-25px);
	}
}


<style scoped>
/* 属性弹窗样式 */
.attr-modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 999;
}
.attr-content {
  background: #fff;
  border-radius: 12px;
  padding: 20px;
  width: 320px;
  max-width: 90%;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}
.attr-title {
  margin: 0 0 12px 0;
  font-size: 18px;
  color: #333;
}
.attr-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px 12px;
}
.attr-item {
  background: #f7f7f7;
  border-radius: 6px;
  padding: 8px 10px;
  display: flex;
  justify-content: space-between;
  font-size: 14px;
  color: #333;
}
.attr-item.full {
  grid-column: 1 / -1;
}
.close-btn {
  margin-top: 12px;
  width: 100%;
  padding: 8px 12px;
  background: #2980b9;
  color: #fff;
  border: none;
  border-radius: 6px;
  cursor: pointer;
}
/* 状态效果样式 */
.status-effects {
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
	margin-top: 4px;
	justify-content: center;
}

.status-effect {
	background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
	color: white;
	font-size: 10px;
	padding: 2px 6px;
	border-radius: 8px;
	white-space: nowrap;
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
	/* 移除状态效果的闪动动画 */
}

@keyframes statusGlow {
	from {
		box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
	}
	to {
		box-shadow: 0 1px 8px rgba(102, 126, 234, 0.4);
	}
}

/* 移动端优化 */
@media screen and (max-width: 768px) {
	.battle-container {
		padding: 3px 4px;
		padding-top: calc(3px + env(safe-area-inset-top, 0px));
		padding-bottom: calc(3px + env(safe-area-inset-bottom, 0px));
	}
	
	.battle-header {
		padding: 4px 8px;
		margin-bottom: 3px;
	}
	
	.battle-title {
		font-size: 12px;
	}
	
	.round-display {
		font-size: 10px;
		padding: 3px 8px;
	}
	
	.speed-btn, .back-btn, .restart-btn {
		padding: 3px 6px;
		font-size: 10px;
	}
	
	.speed-select {
		padding: 3px 6px;
		font-size: 10px;
	}
	
	.enemy-area, .player-area {
		padding: 3px 5px;
		margin-bottom: 2px;
		max-height: 40%; /* 移动端进一步限制高度 */
	}
	
	.area-title {
		font-size: 11px;
		margin-bottom: 1px;
		padding: 1px 0;
	}
	
	.unit {
		flex: 1 1 calc((100% - 12px) / 3);
		max-width: calc((100% - 12px) / 3);
		height: 100%; /* 占满行高 */
		min-height: 105px; /* 移动端大幅提高最小高度 */
		padding: 12px 4px; /* 增加上下padding */
	}
	
	.unit-name {
		font-size: 11px; /* 增加移动端字体 */
		line-height: 1.3;
		margin: 2px 0;
	}
	
	.hp-text {
		font-size: 9px; /* 增加移动端字体 */
	}
	
	.hp-bar {
		height: 5px; /* 增加移动端血条高度 */
	}
	
	.action-log-display {
		padding: 8px 12px;
		/* 移动端固定高度 */
		height: 90px;
		min-height: 90px;
		max-height: 90px;
		margin: 3px auto;
	}
	
	.action-log-content {
		font-size: 13px;
	}
	
	.action-round-info {
		font-size: 16px;
	}
	
	.actor-name {
		font-size: 14px;
		padding: 3px 8px;
	}
	
	.action-type {
		font-size: 13px;
		padding: 3px 8px;
	}
	
	.action-skill {
		font-size: 14px;
		padding: 3px 8px;
	}
	
	.action-description {
		font-size: 13px;
		line-height: 1.4;
	}
	
	.log-toggle-btn-compact {
		padding: 6px 10px;
		height: 32px;
		min-width: 40px;
		font-size: 11px;
	}
	
	.log-toggle-btn-compact .log-icon {
		font-size: 14px;
	}
}

/* 保持原样式，新增选择器样式 */
.speed-select {
	appearance: none;
	padding: 4px 8px;
	border: 1px solid rgba(0,0,0,0.1);
	border-radius: 4px;
	background: #fff;
	font-size: 11px;
	cursor: pointer;
	white-space: nowrap;
}
</style>